#####################################
#           Light plugin            #
#       Made by loliconshik3        #
#                                   #
#   List of supported bulb models:  #
# - TP-Link Tapo:                   #
#       - P100                      #
#       - P110                      #
#       - L530(E)                   #
#       - Another with same API     #
#                                   #
#   Supported by PyP100 package     #
#####################################

from plugins.plugin import Plugin
from plugins.item import Item

from PyP100 import PyL530, PyP100, PyP110
import colorsys

class Light(Plugin):

    def __init__(self, config=None) -> None:
        super().__init__(config)
        self.name = "Light"
        self.bulb = None
        self.save_config()
        self.login()

    def get_model(self):
        """
        Find model (from config) in supported models and return model class
        """
        models = self.get_models()
        model_name = self.config.get_plugin_option(self.name, 'model')
        if model_name in models.keys():
            return models[model_name]
        else: return None

    def get_models(self) -> dict:
        """
        Dict of supported models
        """
        return {
            "L530" : PyL530.L530,
            "P100" : PyP100.P100,
            "P110" : PyP110.P110
        }

    def login(self, data: str="") -> bool:
        """
        Try to login with data (ip:login:password)
        If data is empty - try to login with config data
        """
        if data in ["", "None", None]:
            ip          = self.config.get_plugin_option(self.name, 'ip')
            login       = self.config.get_plugin_option(self.name, 'login')
            password    = self.config.get_plugin_option(self.name, 'password')
            data        = f'{ip}:{login}:{password}'
        data = data.split(':')
        try:
            model = self.get_model()
            self.bulb = model(data[0], data[1], data[2])
            self.bulb.handshake()
            self.bulb.login()
            return True
        except Exception as e: 
            print(e)
            self.bulb = None
            return False

    def turn_off(self) -> bool:
        """
        If bulb is login - turn off it
        """
        if self.bulb is None: return False
        self.bulb.turnOff()
        return True

    def turn_on(self) -> bool:
        """
        If bulb is login - turn on it
        """
        if self.bulb is None: return False
        self.bulb.turnOn()
        return True

    def toggle_state(self) -> bool:
        """
        If bulb is login - toggle his state
        on -> off 
        off -> on
        """
        if self.bulb is None: return False
        self.bulb.toggleState()
        return True

    def set_brightness(self, brightness) -> bool:
        """
        If bulb is login - try to convert str brightness to int
        And set bulb brightness

        API does not support 1 digit values (1, 2, 3 ... 9)
        We need to convert 1dv to float
        """
        if self.bulb is None: return False
        try: brightness = int(brightness)
        except Exception as e: 
            print(e); return False
        else:
            if brightness < 10: brightness = float(brightness)
            self.bulb.setBrightness(brightness)
            return True

    def set_color(self, color: str) -> bool:
        """
        :param color - hex color code (#ffffff)

        If bulb is login - convert hex to rgb
        Then convert rgb to hsv (hue, saturation, value)
        And set color of bulb by hue*360 and saturation*100
        """
        if self.bulb is None: return False
        rgb_tupple = tuple(int(color[i:i+2], 16) for i in (0, 2, 4))
        hue, saturation, _ = colorsys.rgb_to_hsv(rgb_tupple[0], rgb_tupple[1], rgb_tupple[2])
        self.bulb.setColor(hue*360, saturation*100)
        return True

    def set_color_temp(self, temp) -> bool:
        """
        If bulb is login - try to convert temp str to int
        Then set color temp (from 2500K to 6500K)
        """
        if self.bulb is None: return False
        try: temp = int(temp)
        except Exception as e: print(e); return False
        if temp < 2500 or temp > 6500: return False
        self.bulb.setColorTemp(temp)
        return True

    def get_items(self) -> list:
        """
        Return list of plugin actions with options:
        Item(name_of_item, item_action, is_input_required).convert_to_json()
        name_of_item        - Readable name for users
        item_action         - Name of method in this class for action
        is_input_required   - Is user need to send some input? 
        """
        login_title = "Login (-)" if self.bulb is None else "Login (+)"
        return [ 
            Item(login_title,       'login',            True).to_json(),
            Item("Turn off",        'turn_off',         False).to_json(),
            Item("Turn on",         'turn_on',          False).to_json(),
            Item("Toggle state",    'toggle_state',     False).to_json(),
            Item('Set brightness',  'set_brightness',   True).to_json(),
            Item('Set color',       'set_color',        True).to_json(),
            Item('Set color temp',  'set_color_temp',   True).to_json(),
        ]

    def save_config(self) -> None:
        """
        Save options to main config
        """
        self.config.add_plugin_option(self.name, "model",       "P100")
        self.config.add_plugin_option(self.name, "ip",          "192.168.1.1")
        self.config.add_plugin_option(self.name, "login",       "your-login")
        self.config.add_plugin_option(self.name, "password",    "your-password")

